﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Configuration;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading;
using System.Web;
using System.Web.Configuration;
using System.Web.Mvc;
using System.Web.Routing;
using System.Web.Script.Serialization;
using System.Web.Security;
using System.Web.WebPages;
using System.Xml;
using System.Xml.Serialization;
//using EFJune::System.Data.Entity.Validation;
using Elmah;
using Newtonsoft.Json;
using Newtonsoft.Json.Converters;
//using System.Data.Entity.Validation;
//using System.ServiceModel.Syndication;
//using System.ServiceModel.Web;
//using Elmah;
//using Newtonsoft.Json;
//using Newtonsoft.Json.Converters;

namespace System
{
	public enum SerializableFormat { JSON, JSONP, XML, HTML };
	/// <summary>The ExtendedMethods class</summary>
	/// <created author="laurentiu.macovei" date="Thu, 16 Jun 2011 15:09:44 GMT"/>
	public static class ExtendedMethods
	{

		/// <summary>Encodes a string and returns it  as MvcHtmlString.</summary>
		/// <param name="text">A string to encode.</param>
		/// <param name="addDoubleQuotes">
		/// A value that indicates whether double quotation marks will be included around 
		/// the encoded string. 
		/// </param>
		/// <returns>An encoded string.</returns>
		/// <created author="laurentiu.macovei" date="Mon, 13 Feb 2012 15:40:38 GMT"/>
		public static MvcHtmlString js(this string text, bool addDoubleQuotes = true)
		{
			return new MvcHtmlString(HttpUtility.JavaScriptStringEncode(text, addDoubleQuotes));
		}

		/// <summary>Encodes a string and returns it as String.</summary>
		/// <param name="text">A string to encode.</param>
		/// <param name="addDoubleQuotes">
		/// A value that indicates whether double quotation marks will be included around 
		/// the encoded string. 
		/// </param>
		/// <returns>An encoded string.</returns>
		/// <created author="laurentiu.macovei" date="Mon, 13 Feb 2012 15:40:38 GMT"/>
		public static string Js(this string text, bool addDoubleQuotes = true)
		{
			return HttpUtility.JavaScriptStringEncode(text, addDoubleQuotes);
		}
		#region Fields


		#region Const

		public static readonly Type MonoRuntime = Type.GetType("Mono.Runtime", false);
		public static readonly bool RunningOnMONO = MonoRuntime != null;

		#endregion Const


		#region Static

		private static Func<Exception, string> printException
							= e => e == null
								? null
								: e.Message + (e.InnerException == null ? null : @"
 [Inner Exception]: " + printException(e.InnerException));


		#endregion Static


		#endregion Fields


		#region Business Methods

		/// <summary>Sets the properties of another dictionary or anonymous object into this dictionary</summary>
		/// <param name="dictionary">The dictionary to set the values into</param>
		/// <param name="anonymousObject">An anonymous object or IDictionary&lt;string,object&gt; to copy the properties from</param>
		/// <returns>The original dictionary updated</returns>
		/// <created author="laurentiu.macovei" date="Fri, 06 Jan 2012 18:44:48 GMT"/>
		public static T SetValues<T>(this T dictionary, object anonymousObject)
			where T : IDictionary<string, object>
		{
			if (anonymousObject != null && dictionary != null)
			{
				var dic = anonymousObject as IDictionary<string, object>;
				if (dic != null)
				{
					foreach (var item in dic)
						dictionary[item.Key] = item.Value;
				}
				else
				{
					foreach (PropertyDescriptor descriptor in TypeDescriptor.GetProperties(anonymousObject))
					{
						object obj2 = descriptor.GetValue(anonymousObject);
						dictionary[descriptor.Name] = obj2;
					}
				}
			}
			return dictionary;
		}
		/// <summary>Sets the properties of another dictionaries or anonymous objects into this dictionary</summary>
		/// <param name="dictionary">The dictionary to set the values into</param>
		/// <param name="anonymousObjects">Any anonymous objects or IDictionary&lt;string,object&gt; to copy the properties from</param>
		/// <returns>The original dictionary updated</returns>
		/// <created author="laurentiu.macovei" date="Fri, 06 Jan 2012 18:49:11 GMT"/>
		public static T SetValues<T>(this T dictionary, params object[] anonymousObjects)
			where T : IDictionary<string, object>
		{
			if (anonymousObjects != null && dictionary != null)
			{
				foreach (var anonymousObject in anonymousObjects)
				{

					var dic = anonymousObject as IDictionary<string, object>;
					if (dic != null)
					{
						foreach (var item in dic)
							dictionary[item.Key] = item.Value;
					}
					else
					{
						foreach (PropertyDescriptor descriptor in TypeDescriptor.GetProperties(anonymousObject))
						{
							object obj2 = descriptor.GetValue(anonymousObject);
							dictionary[descriptor.Name] = obj2;
						}
					}
				}
			}
			return dictionary;
		}
		/// <summary>Sets the properties of another dictionary or anonymous object into this dictionary</summary>
		/// <param name="dictionary">The dictionary to set the values into</param>
		/// <param name="htmlAttributes">Specify true in order to detect the css class properties and concatenate them</param>
		/// <param name="anonymousObject">An anonymous object or IDictionary&lt;string,object&gt; to copy the properties from</param>
		/// <returns>The original dictionary updated</returns>
		/// <created author="laurentiu.macovei" date="Fri, 13 Jan 2012 02:35:09 GMT"/>
		public static T SetValues<T>(this T dictionary, bool htmlAttributes, object anonymousObject)
			where T : IDictionary<string, object>
		{
			if (!htmlAttributes)
				return SetValues(dictionary, anonymousObject);
			if (anonymousObject != null && dictionary != null)
			{
				var dic = anonymousObject as IDictionary<string, object>;
				if (dic != null)
				{
					foreach (var item in dic)
						dictionary[item.Key] = item.Value;
				}
				else
				{
					foreach (PropertyDescriptor descriptor in TypeDescriptor.GetProperties(anonymousObject))
					{
						object obj2 = descriptor.GetValue(anonymousObject);
						dictionary[descriptor.Name.Replace('_', '-')] = obj2;
					}
				}
			}
			return dictionary;
		}
		/// <summary>Sets the properties of another dictionaries or anonymous objects into this dictionary</summary>
		/// <param name="dictionary">The dictionary to set the values into</param>
		/// <param name="htmlAttributes">The dictionary to get the values from</param>
		/// <param name="anonymousObjects">Any anonymous objects or IDictionary&lt;string,object&gt; to copy the properties from</param>
		/// <returns>The original dictionary updated</returns>
		/// <created author="laurentiu.macovei" date="Fri, 13 Jan 2012 02:35:09 GMT"/>
		public static T SetValues<T>(this T dictionary, bool htmlAttributes, params object[] anonymousObjects)
			where T : IDictionary<string, object>
		{
			if (!htmlAttributes)
				return SetValues(dictionary, anonymousObjects);
			if (anonymousObjects != null && dictionary != null)
			{
				foreach (var anonymousObject in anonymousObjects)
				{

					var dic = anonymousObject as IDictionary<string, object>;
					if (dic != null)
					{
						foreach (var item in dic)
						{
							var key = item.Key;
							var value = item.Value;
							dictionary[key] = "class".Equals(key, StringComparison.OrdinalIgnoreCase)
								? (string.IsNullOrWhiteSpace(value as string) || string.IsNullOrWhiteSpace(dictionary[key] as string) ? value : dictionary[key] as string + " " + (value ?? string.Empty).ToString())
								: value;
						}

					}
					else
					{
						foreach (PropertyDescriptor descriptor in TypeDescriptor.GetProperties(anonymousObject))
						{
							var key = descriptor.Name;
							var value = descriptor.GetValue(anonymousObject);
							dictionary[key.Replace('_', '-')] = "class".Equals(key, StringComparison.OrdinalIgnoreCase)
								? (string.IsNullOrWhiteSpace(value as string) || string.IsNullOrWhiteSpace(dictionary[key] as string) ? value : dictionary[key] as string + " " + (value ?? string.Empty).ToString())
								: value;
						}
					}
				}
			}
			return dictionary;
		}

		/// <summary>Returns the SerializationFormat enum value from its string representance</summary>
		/// <param name="_format">The format to serialize the results to. Known formats: json, jsonp, xml. The default format is json</param>
		/// <created author="laurentiu.macovei" date="Thu, 16 Jun 2011 22:19:47 GMT"/>
		public static SerializableFormat GetSerializationFormat(string _format)
		{
			SerializableFormat format;
			switch ((_format ?? "").ToLower())
			{
				case "xml":
					format = SerializableFormat.XML;
					break;
				case "html":
					format = SerializableFormat.HTML;
					break;
				case "jsonp":
					format = SerializableFormat.JSONP;
					break;
				case "json":
				default:
					format = SerializableFormat.JSON;
					break;
			}
			return format;
		}

		/// <summary>Returns true if the provided value is contained in the given items</summary>
		/// <param name="value">The value to be searched</param>
		/// <param name="items">The items to search throguh</param>
		/// <created author="laurentiu.macovei" date="Fri, 08 Jul 2011 22:47:27 GMT"/>
		public static bool In<T>(this T value, params T[] items)
		{
			return items != null && items.Length > 0 && items.Contains(value);
		}

		/// <summary>Returns true if the provided value is contained in the given items</summary>
		/// <param name="value">The value to be searched</param>
		/// <param name="items">The items to search throguh</param>
		/// <param name="comparer">An equality comparer to compare values.</param>
		/// <created author="laurentiu.macovei" date="Sat, 12 Nov 2011 20:22:14 GMT"/>
		public static bool In<T>(this T value, IEqualityComparer<T> comparer, params T[] items)
		{
			return items != null && items.Length > 0 && items.Contains(value, comparer);
		}


		///// <summary>Serializes the given object result to a specified format.</summary>
		///// <param name="result">The result object to be serialized in the given format</param>
		///// <param name="format">Optional. The format to serialize the results to. Known formats: json, jsonp, xml. The default format is json</param>
		///// <param name="callback">Optional. Specify a custom callback when used in conjunction with JSONP format. The default callback is 'callback'</param>
		///// <returns></returns>
		///// <created author="laurentiu.macovei" date="Mon, 25 Jul 2011 16:21:08 GMT"/>
		//public static string SerializeObject(this object result, SerializableFormat format = SerializableFormat.JSON, string callback = null, string xmlRootNode = null)
		//{
		//    string contentType;
		//    return SerializeObject(result, out contentType, format, callback, xmlRootNode);
		//}

		///// <summary>Serializes the given object result to a specified format. It also returns the output contentType</summary>
		///// <param name="result">The result object to be serialized in the given format</param>
		///// <param name="format">Optional. The format to serialize the results to. Known formats: json, jsonp, xml. The default format is json</param>
		///// <param name="callback">Optional. Specify a custom callback when used in conjunction with JSONP format. The default callback is 'callback'</param>
		///// <returns></returns>
		///// <created author="laurentiu.macovei" date="Mon, 25 Jul 2011 16:21:09 GMT"/>
		//public static string SerializeObject(this object result, out string contentType, SerializableFormat format = SerializableFormat.JSON, string callback = null, string xmlRootNode = null)
		//{
		//    StringBuilder sb = null;
		//    switch (format)
		//    {
		//        case SerializableFormat.XML:
		//            {
		//                sb = new StringBuilder();
		//                var json = JsonConvert.SerializeObject(result is IEnumerable ? new { item = result } : result);
		//                var myXmlNode = JsonConvert.DeserializeXmlNode(json, xmlRootNode);
		//                myXmlNode.Save(new StringWriter(sb));
		//                contentType = "text/xml";
		//            }
		//            break;
		//        case SerializableFormat.JSONP:
		//            {
		//                sb = new StringBuilder();
		//                var json = JsonConvert.SerializeObject(new { d = result }, Newtonsoft.Json.Formatting.Indented, new JsonSerializerSettings() { DefaultValueHandling = DefaultValueHandling.Ignore, Converters = { new IsoDateTimeConverter() { DateTimeFormat = "s", DateTimeStyles = System.Globalization.DateTimeStyles.AdjustToUniversal } } });
		//                sb.Append(callback ?? "callback").Append("( ").Append(json).Append(" );");
		//                contentType = "application/javascript";
		//            }
		//            break;
		//        case SerializableFormat.HTML:
		//            {
		//                sb = new StringBuilder();
		//                var json = JsonConvert.SerializeObject(new { d = result }, Newtonsoft.Json.Formatting.Indented, new JsonSerializerSettings() { DefaultValueHandling = DefaultValueHandling.Ignore, Converters = { new IsoDateTimeConverter() { DateTimeFormat = "s", DateTimeStyles = System.Globalization.DateTimeStyles.AdjustToUniversal } } });
		//                sb.Append("<!doctype html><html><body><pre>")
		//                    .Append(json)
		//                    .Append("</pre></body></html>");
		//                contentType = "text/html";
		//            }
		//            break;
		//        default:// use any other format as json
		//        case SerializableFormat.JSON:
		//            {
		//                var json = JsonConvert.SerializeObject(new { d = result }, Newtonsoft.Json.Formatting.Indented, new JsonSerializerSettings() { DefaultValueHandling = DefaultValueHandling.Ignore, Converters = { new IsoDateTimeConverter() { DateTimeFormat = "s", DateTimeStyles = System.Globalization.DateTimeStyles.AdjustToUniversal } } });
		//                contentType = "application/json";
		//                return json;
		//            }
		//    }
		//    return sb.ToString();
		//}

		///// <summary>Serializes the given object result to a specified format.</summary>
		///// <param name="result">The result object to be serialized in the given format</param>
		///// <param name="format">Optional. The format to serialize the results to. Known formats: json, jsonp, xml. The default format is json</param>
		///// <param name="callback">Optional. Specify a custom callback when used in conjunction with JSONP format. The default callback is 'callback'</param>
		///// <created author="laurentiu.macovei" date="Thu, 28 Jul 2011 05:29:48 GMT"/>
		//public static MemoryStream GetServiceStream(this object result, string format, string callback = null, string xmlRootNode = "d")
		//{
		//    return GetServiceStream(result, GetSerializationFormat(format), callback, xmlRootNode);
		//}

		///// <summary>Serializes the given object result to a specified format.</summary>
		///// <param name="result">The result object to be serialized in the given format</param>
		///// <param name="format">Optional. The format to serialize the results to. Known formats: json, jsonp, xml. The default format is json</param>
		///// <param name="callback">Optional. Specify a custom callback when used in conjunction with JSONP format. The default callback is 'callback'</param>
		///// <created author="laurentiu.macovei" date="Thu, 16 Jun 2011 15:09:44 GMT"/>
		//public static MemoryStream GetServiceStream(this object result, SerializableFormat format = SerializableFormat.JSON, string callback = null, string xmlRootNode = "d")
		//{
		//    MemoryStream stream = new MemoryStream();
		//    StreamWriter writer = new StreamWriter(stream, Encoding.UTF8);
		//    string contentType;
		//    switch (format)
		//    {
		//        case SerializableFormat.XML:
		//            {
		//                //XmlSerializer xmls = new XmlSerializer(result.GetType());
		//                //xmls.Serialize(writer, result);
		//                var json = JsonConvert.SerializeObject(result is IEnumerable ? new { item = result } : result);
		//                var myXmlNode = JsonConvert.DeserializeXmlNode(json, xmlRootNode);
		//                myXmlNode.Save(writer);
		//                contentType = "text/xml";
		//            }
		//            break;
		//        case SerializableFormat.JSONP:
		//            {
		//                var json = JsonConvert.SerializeObject(new { d = result }, Newtonsoft.Json.Formatting.Indented, new JsonSerializerSettings() { DefaultValueHandling = DefaultValueHandling.Ignore, Converters = { new IsoDateTimeConverter() { DateTimeFormat = "s", DateTimeStyles = System.Globalization.DateTimeStyles.AdjustToUniversal } } });
		//                writer.Write((callback ?? "callback") + "( " + json + " );");
		//                contentType = "application/javascript";
		//            }
		//            break;
		//        case SerializableFormat.HTML:
		//            {
		//                var json = JsonConvert.SerializeObject(new { d = result }, Newtonsoft.Json.Formatting.Indented, new JsonSerializerSettings() { DefaultValueHandling = DefaultValueHandling.Ignore, Converters = { new IsoDateTimeConverter() { DateTimeFormat = "s", DateTimeStyles = System.Globalization.DateTimeStyles.AdjustToUniversal } } });
		//                writer.WriteLine("<!doctype html><html><body><pre>");
		//                writer.WriteLine(json);
		//                writer.Write("</pre></body></html>");
		//                contentType = "text/html";
		//            }
		//            break;
		//        default:// use any other format as json
		//        case SerializableFormat.JSON:
		//            {
		//                var json = JsonConvert.SerializeObject(new { d = result }, Newtonsoft.Json.Formatting.Indented, new JsonSerializerSettings() { DefaultValueHandling = DefaultValueHandling.Ignore, Converters = { new IsoDateTimeConverter() { DateTimeFormat = "s", DateTimeStyles = System.Globalization.DateTimeStyles.AdjustToUniversal } } });
		//                writer.Write(json);
		//                contentType = "application/json";
		//            }
		//            break;
		//    }
		//    var operationContext = WebOperationContext.Current;
		//    if (operationContext != null && operationContext.OutgoingResponse != null)
		//        operationContext.OutgoingResponse.ContentType = contentType;
		//    var httpContext = HttpContext.Current;
		//    if (httpContext != null && httpContext.Response != null)
		//        httpContext.Response.ContentType = contentType;
		//    writer.Flush();
		//    stream.Position = 0;
		//    return stream;
		//}

		/// <summary>
		/// </summary>
		/// <param name="list"></param>
		/// <param name="lookup"></param>
		/// <returns></returns>
		/// <created author="laurentiu.macovei" date="Sat, 03 Mar 2012 02:59:14 GMT"/>
		public static IEnumerable<T> Distinct<T, TKey>(this IEnumerable<T> list, Func<T, TKey> lookup) where TKey : struct
		{
			return list.Distinct(new StructEqualityComparer<T, TKey>(lookup));
		}


		/// <summary>The StructEqualityComparer class</summary>
		/// <created author="laurentiu.macovei" date="Sat, 03 Mar 2012 02:59:17 GMT"/>
		class StructEqualityComparer<T, TKey> 
		: IEqualityComparer<T> where TKey : struct
		{

			Func<T, TKey> lookup;

			/// <summary>Creates a new instance of StructEqualityComparer</summary>
			/// <param name="lookup"></param>
			/// <created author="laurentiu.macovei" date="Sat, 03 Mar 2012 02:59:17 GMT"/>
			public StructEqualityComparer(Func<T, TKey> lookup)
			{
				this.lookup = lookup;
			}

			/// <summary>
			/// </summary>
			/// <param name="x"></param>
			/// <param name="y"></param>
			/// <returns></returns>
			/// <created author="laurentiu.macovei" date="Sat, 03 Mar 2012 02:59:17 GMT"/>
			public bool Equals(T x, T y)
			{
				return lookup(x).Equals(lookup(y));
			}

			/// <summary>
			/// </summary>
			/// <param name="obj"></param>
			/// <returns></returns>
			/// <created author="laurentiu.macovei" date="Sat, 03 Mar 2012 02:59:18 GMT"/>
			public int GetHashCode(T obj)
			{
				return lookup(obj).GetHashCode();
			}
		}
		/// <summary>Returns an exception message with all of inner exceptions</summary>
		/// <param name="exception">The exception to be printed out</param>
		/// <created author="laurentiu.macovei" date="Fri, 08 Jul 2011 23:32:04 GMT"/>
		public static string Print(this Exception exception)
		{
			var dbEntityValidationException = (dynamic)(exception as /*EFJune::*/System.Data.Entity.Validation.DbEntityValidationException) ?? exception as global::System.Data.Entity.Validation.DbEntityValidationException;
			if (dbEntityValidationException != null)
			{
				var sb = new StringBuilder("<ul>");
				foreach (var entityValidationError in dbEntityValidationException.EntityValidationErrors)
				{
					if (!entityValidationError.IsValid)
					{
						sb.Append("<li><span>").Append(entityValidationError.Entry.Entity == null ? null : HttpUtility.HtmlEncode(entityValidationError.Entry.Entity.GetType().Name))
							.Append("</span><ul>");
						foreach (var error in entityValidationError.ValidationErrors)
						{
							sb.Append("<li><span>").Append(error.PropertyName).Append("</span> : <span>").Append(error.ErrorMessage).Append("</span></li>");
						}
						sb.Append("</ul></li>");
					}
				}
				sb.Append("</ul>");
				return sb.ToString();
			}
			return printException(exception);
		}
		/// <summary>Returns the message with detailed information like OS, Platform, .NET Framework information</summary>
		/// <param name="success">Specify whether the operation was a success</param>
		/// <param name="message">The message to be returned</param>
		/// <returns>The message serialized as json in a stream</returns>
		/// <created author="laurentiu.macovei" date="Mon, 25 Jul 2011 16:05:23 GMT"/>
		public static InformationMessage GetInformationMessage(bool success, string message)
		{
			var platformVersion = string.Empty;

			if (RunningOnMONO)
			{
				var dispalayName = MonoRuntime.GetMethod("GetDisplayName", BindingFlags.NonPublic | BindingFlags.Static);
				if (dispalayName != null)
					platformVersion = "MONO " + dispalayName.Invoke(null, null) as string;
			}
			else platformVersion = "Windows / .NET Framework";
			return new InformationMessage
			{
				Success = success,
				Platform = platformVersion,
				OS = Environment.OSVersion.ToString(),
				DotNetFramework = Environment.Version.ToString(),
				Message = message
			};
		}


		/// <summary>
		/// </summary>
		/// <param name="lists"></param>
		/// <returns></returns>
		/// <created author="laurentiu.macovei" date="Mon, 25 Jul 2011 23:20:01 GMT"/>
		public static IEnumerable<T> Concatenate<T>(this IEnumerable<T[]> lists)
		{
			if (lists != null)
				foreach (var list in lists)
					if (list != null)
						foreach (var item in list)
							yield return item;
		}

		/// <summary>Encapsulates the given item as an IEnumerable&lt;T&gt;</summary>
		/// <param name="item">The item to be returned as an IEnumerable</param>
		/// <created author="laurentiu.macovei" date="Mon, 25 Jul 2011 23:20:01 GMT"/>
		public static IEnumerable<T> ToEnumerable<T>(this T item)
		{
			yield return item;
		}

		/// <summary>Concatenates the given source with the provided params</summary>
		/// <param name="source">The source to be concatenated from</param>
		/// <param name="items">The items to be concatenated to</param>
		/// <created author="laurentiu.macovei" date="Mon, 25 Jul 2011 23:20:02 GMT"/>
		public static IEnumerable<T> Concat<T>(this IEnumerable<T> source, params T[] items)
		{
			return source.Concat(items == null ? null : items.AsEnumerable());
		}

		/// <summary>Concatenates an element to the given params</summary>
		/// <param name="item">The source item</param>
		/// <param name="items">The additional items to be concatenated</param>
		/// <created author="laurentiu.macovei" date="Mon, 25 Jul 2011 23:20:02 GMT"/>
		public static IEnumerable<T> SelfConcat<T>(this T item, params T[] items)
		{
			return item.ToEnumerable().Concat((IEnumerable<T>)items);
		}

		/// <summary>Concatenates an element to the given items</summary>
		/// <param name="item">The source item</param>
		/// <param name="items">The additional items to be concatenated</param>
		/// <created author="laurentiu.macovei" date="Mon, 25 Jul 2011 23:20:02 GMT"/>
		public static IEnumerable<T> SelfConcat<T>(this T item, IEnumerable<T> items)
		{
			return item.ToEnumerable().Concat(items);
		}

		/// <summary>Concatenates multiple list's elements into one single enumeration</summary>
		/// <param name="lists"></param>
		/// <returns></returns>
		/// <created author="laurentiu.macovei" date="Mon, 25 Jul 2011 19:31:28 GMT"/>
		public static IEnumerable<T> Concatenate<T>(this IEnumerable<IEnumerable<T>> lists)
		{
			if (lists != null)
				foreach (var list in lists)
					if (list != null)
						foreach (var item in list)
							yield return item;
		}

		/// <summary>Serializes the given model state in a json compatible dictionary</summary>
		/// <param name="modelState">The model state to be serialized</param>
		/// <created author="laurentiu.macovei" date="Fri, 18 Mar 2011 02:23:32 GMT"/>
		[DebuggerStepThrough]
		public static Dictionary<string, ModelErrorCollection> ToJson(this ModelStateDictionary modelState)
		{
			return modelState == null ? null : modelState
				.Where(e => e.Value.Errors.Any())
				.ToDictionary(e => e.Key, e => e.Value.Errors);
		}

		/// <summary>Serializes the object as json</summary>
		/// <param name="o"></param>
		/// <returns></returns>
		/// <param name="controller">The controller to be extended</param>
		/// <created author="laurentiu.macovei" date="Sat, 29 Jan 2011 19:01:03 GMT"/>
		public static string ToJson(this Controller controller, object o)
		{
			var serializer = new JavaScriptSerializer();
			var json = serializer.Serialize(o);
			return json;
		}

		/// <summary>Logs an exception and returns it for eventually rethrow</summary>
		/// <param name="ex">The exception beeing logged</param>
		/// <returns>The exception beeing logged</returns>
		/// <created author="laurentiu.macovei" date="Sat, 12 Mar 2011 20:38:07 GMT"/>
		[DebuggerNonUserCode, DebuggerStepThrough]
		public static Exception LogException(this Exception ex)
		{
			var context = HttpContext.Current;
			ErrorLog.GetDefault(context).Log(new Error(ex, context));
			return ex;
		}

		/// <summary>Executes the action and if an error occurs will log it into the system with AppContext.LogException </summary>
		/// <param name="action">The action to be executed</param>
		/// <param name="logExceptionWhenThrown">This action will be executed when an exception was thrown so you can treat it</param>
		/// <param name="logToElmah">Specify whether any catched errors will be logged to ELMAH. Default to true</param>
		/// <created author="tudor.dumitriu" date="Thu, 05 May 2011 15:06:22 GMT"/>
		[DebuggerStepThrough]
		public static void CatchException(Action action, Action<Exception> logExceptionWhenThrown = null, bool logToElmah = true)
		{
			try
			{
				if (action != null)
					action();
			}
			catch (Exception ex)
			{
				if (logToElmah)
					LogException(ex);
				if (logExceptionWhenThrown != null)
					logExceptionWhenThrown(ex);
			}
		}

		/// <summary>Executes the action and if an error occurs will log it into the system with AppContext.LogException </summary>
		/// <param name="action">The action to be executed</param>
		/// <param name="logExceptionWhenThrown">This action will be executed when an exception was thrown so you can treat it</param>
		/// <param name="logToElmah">Specify whether any catched errors will be logged to ELMAH. Default to true</param>
		/// <created author="gabriel.pagu" date="Mon, 04 Apr 2011 21:52:52 GMT"/>
		[DebuggerStepThrough]
		public static T CatchException<T>(Func<T> action, Action<Exception> logExceptionWhenThrown = null, bool logToElmah = true)
		{
			try
			{
				if (action != null)
					return action();
			}
			catch (Exception ex)
			{
				if (logToElmah)
					LogException(ex);
				if (logExceptionWhenThrown != null)
					logExceptionWhenThrown(ex);
			}
			return default(T);
		}
		/// <summary>Executes the action and if an error occurs will log it into the system with AppContext.LogException </summary>
		/// <param name="action">The action to be executed</param>
		/// <param name="logExceptionWhenThrown">This action will be executed when an exception was thrown so you can treat it</param>
		/// <param name="logToElmah">Specify whether any catched errors will be logged to ELMAH. Default to true</param>
		/// <created author="gabriel.pagu" date="Mon, 04 Apr 2011 21:52:52 GMT"/>
		[DebuggerStepThrough]
		public static T CatchException<T>(Func<T> action, Func<Exception, T> logExceptionWhenThrown = null, bool logToElmah = true)
		{
			try
			{
				if (action != null)
					return action();
			}
			catch (Exception ex)
			{
				if (logToElmah)
					LogException(ex);
				if (logExceptionWhenThrown != null)
					return logExceptionWhenThrown(ex);
			}
			return default(T);
		}
		/// <summary>Returns a localized message like: An error has occured. We have been notified and we will fix it shortly. Thank you for your patience!</summary>
		/// <created author="laurentiu.macovei" date="Mon, 13 Feb 2012 11:14:46 GMT"/>
		[DebuggerStepThrough]
		public static string ApologizeErrorMessage()
		{
			return CultureInfo.CurrentCulture._("An error has occured. We have been notified and we will fix it shortly. Thank you for your patience!") + "\n";
		}
		/// <summary>Returns a string array that contains the substrings in this string that are delimited by elements of a specified Unicode character array. A parameter specifies whether to return empty array elements.</summary>
		/// <param name="value">The string value to be split</param>
		/// <param name="options">Specify System.StringSplitOptions.RemoveEmptyEntries to omit empty array elements from the array returned, or System.StringSplitOptions.None to include empty array elements in the array returned.</param>
		/// <param name="separators">An array of Unicode characters that delimit the substrings in this string, an empty array that contains no delimiters, or null.</param>
		/// <returns></returns>
		/// <created author="laurentiu.macovei" date="Sat, 20 Mar 2010 16:51:23 GMT"/>
		public static string[] Split(this string value, StringSplitOptions options, params char[] separators)
		{
			if (value == null)
				return null;
			return value.Split(separators, options);
		}
		/// <summary>Returns a string array that contains the substrings in this string that are delimited by elements of a specified Unicode character. A parameter specifies whether to return empty array elements.</summary>
		/// <param name="value">The string value to be split</param>
		/// <param name="separator">An Unicode character that delimit the substrings in this string</param>
		/// <param name="options">Specify System.StringSplitOptions.RemoveEmptyEntries to omit empty array elements from the array returned, or System.StringSplitOptions.None to include empty array elements in the array returned.</param>
		/// <returns></returns>
		/// <created author="laurentiu.macovei" date="Sat, 20 Mar 2010 16:51:23 GMT"/>
		public static string[] Split(this string value, char separator, StringSplitOptions options)
		{
			return Split(value, options, separator);
		}

		/// <summary>Returns a string array that contains the substrings in this instance that are delimited by elements of a specified Unicode character. A parameter specifies the maximum number of substrings to return.</summary>
		/// <param name="value">The string value to be split</param>
		/// <param name="separator">An Unicode character that delimit the substrings in this string</param>
		/// <param name="count">The maximum number of substrings to return.</param>
		/// <returns></returns>
		/// <created author="laurentiu.macovei" date="Sat, 20 Mar 2010 16:56:28 GMT"/>
		public static string[] Split(this string value, char separator, int count)
		{
			return Split(value, count, separator);
		}
		/// <summary>Returns a string array that contains the substrings in this instance that are delimited by elements of a specified Unicode character array. A parameter specifies the maximum number of substrings to return.</summary>
		/// <param name="value">The string value to be split</param>
		/// <param name="separators">An array of Unicode characters that delimit the substrings in this string, an empty array that contains no delimiters, or null.</param>
		/// <param name="count">The maximum number of substrings to return.</param>
		/// <returns></returns>
		/// <created author="laurentiu.macovei" date="Sat, 20 Mar 2010 16:56:28 GMT"/>
		public static string[] Split(this string value, int count, params char[] separators)
		{
			if (value == null)
				return null;
			return value.Split(separators, count);
		}

		/// <summary>Starts a zone where EditorFor will automatically generate data-bind attributes</summary>
		/// <param name="html"></param>
		/// <param name="htmlAttributes">Any anonymous objects / dictionary with attributes to be added on the provided tag</param>
		/// <param name="tag">Specify any tag to be rendered out. Specify null for no tag to be written out</param>
		/// <returns></returns>
		/// <created author="laurentiu.macovei" date="Fri, 13 Jan 2012 02:35:09 GMT"/>
		public static HelperResult BeginKnockout(this HtmlHelper html, string tag = null, params object[] htmlAttributes)
		{
			var bag = html.ViewContext.Controller.ViewBag;
			var stack = bag.Knockout as Stack<TagBuilder> ?? ((bag.Knockout = new Stack<TagBuilder>()) as Stack<TagBuilder>);
			var tagBuilder = string.IsNullOrEmpty(tag) ? null : new TagBuilder(tag);
			if (tagBuilder != null)
			{
				if (htmlAttributes != null && htmlAttributes.Length > 0)
					foreach (var obj in htmlAttributes)
						tagBuilder.MergeAttributes(obj as IDictionary<string, object> ?? HtmlHelper.AnonymousObjectToHtmlAttributes(obj));
			}
			stack.Push(tagBuilder);
			return new HelperResult(o => o.Write(tagBuilder.ToMvcHtmlString(TagRenderMode.StartTag)));
		}

		/// <summary>Ends a zone where EditorFor will automatically generate data-bind attributes</summary>
		/// <param name="html"></param>
		/// <returns></returns>
		/// <created author="laurentiu.macovei" date="Wed, 25 Jan 2012 22:13:56 GMT"/>
		public static HelperResult EndKnockout(this HtmlHelper html)
		{
			var bag = html.ViewContext.Controller.ViewBag;
			var stack = bag.Knockout as Stack<TagBuilder>;
			if (stack != null && stack.Any())
			{
				var tagBuilder = stack.Pop();
				if (tagBuilder != null)
					return new HelperResult(o => o.Write(tagBuilder.ToString(TagRenderMode.EndTag)));
				return EmptyResult;
			}
			else throw new InvalidOperationException("BeginKnockout was not called!");
		}
		private static readonly HelperResult EmptyResult = new HelperResult(o => { });
		///// <summary>
		///// Starts a zone where EditorFor will automatically generate data-bind attributes
		///// </summary>
		///// <param name="controller"></param>
		///// <returns></returns>
		///// <created author="laurentiu.macovei" date="Fri, 13 Jan 2012 02:35:09 GMT"/>
		//public static IDisposable BeginKnockout(this ControllerBase controller)
		//{
		//    return new KnockoutZone(controller);
		//}
		/// <summary>
		/// Returns true if called in a context where BeginKnockout was called
		/// thus if true EditorFor will automatically generate data-bind attributes
		/// </summary>
		/// <param name="controller"></param>
		/// <returns></returns>
		/// <created author="laurentiu.macovei" date="Fri, 13 Jan 2012 02:35:09 GMT"/>
		public static bool CanOutputKnockout(this ControllerBase controller)
		{
			return (controller.ViewBag.Knockout as Stack<TagBuilder> ?? EmptyStackTagBuilder).Any();
		}
		private static readonly Stack<TagBuilder> EmptyStackTagBuilder = new Stack<TagBuilder>();

		#endregion Business Methods

		#region IsTrue
		/// <summary>Returns true if the value is 1 or true, or default value if null or string.Empty, otherwise false</summary>
		/// <param name="value"></param>
		/// <param name="defaultValue"></param>
		/// <returns></returns>
		/// <created author="laurentiu.macovei" date="Tue, 24 Jul 2007 16:33:34 GMT"/>
		public static bool IsTrue(this string value, bool defaultValue = false)
		{
			if (string.IsNullOrEmpty(value))
				return defaultValue;
			return value == "1" || "true".Equals(value, StringComparison.OrdinalIgnoreCase)
				|| "!false".Equals(value, StringComparison.OrdinalIgnoreCase);
		}
		#endregion IsTrue

		/// <summary>Returns the number used by javascript to build a date<para>(date - 1970/1/1).TotalMiliseconds</para></summary>
		/// <param name="date">The date to be converted</param>
		/// <param name="convertToUtc">Specify whether the date should be converted to UTC (defaults to true)</param>
		/// <created author="laurentiu.macovei" date="Thu, 09 Feb 2012 15:28:46 GMT"/>
		public static long ToJavascriptDateNumber(this DateTime date, bool convertToUtc = true)
		{
			return ConvertDateTimeToJavaScriptTicks(date, convertToUtc);
		}

		/// <summary>
		/// </summary>
		/// <param name="dateTime"></param>
		/// <param name="offset"></param>
		/// <returns></returns>
		/// <created author="laurentiu.macovei" date="Mon, 13 Feb 2012 17:26:47 GMT"/>
		private static long ToUniversalTicks(DateTime dateTime, TimeSpan offset)
		{
			if (dateTime.Kind == DateTimeKind.Utc)
			{
				return dateTime.Ticks;
			}
			long ticks = dateTime.Ticks - offset.Ticks;
			if (ticks > 3155378975999999999L)
			{
				return 3155378975999999999L;
			}
			if (ticks < 0L)
			{
				return 0L;
			}
			return ticks;
		}
		internal const long InitialJavaScriptDateTicks = 621355968000000000L;

		/// <summary>
		/// </summary>
		/// <param name="dateTime"></param>
		/// <param name="offset"></param>
		/// <returns></returns>
		/// <created author="laurentiu.macovei" date="Mon, 13 Feb 2012 17:26:47 GMT"/>
		internal static long ConvertDateTimeToJavaScriptTicks(DateTime dateTime, TimeSpan offset)
		{
			return UniversialTicksToJavaScriptTicks(ToUniversalTicks(dateTime, offset));
		}
		/// <summary>
		/// </summary>
		/// <param name="dateTime"></param>
		/// <returns></returns>
		/// <created author="laurentiu.macovei" date="Mon, 13 Feb 2012 17:26:47 GMT"/>
		internal static long ConvertDateTimeToJavaScriptTicks(DateTime dateTime)
		{
			return ConvertDateTimeToJavaScriptTicks(dateTime, true);
		}

		/// <summary>Converts the number meaning javascriptTicks to DateTime</summary>
		/// <param name="javaScriptTicks">The ticks to be converted</param>
		/// <created author="laurentiu.macovei" date="Mon, 13 Feb 2012 17:32:51 GMT"/>
		public static DateTime ConvertJavaScriptTicksToDateTime(this long javaScriptTicks)
		{
			return new DateTime((javaScriptTicks * 10000L) + InitialJavaScriptDateTicks, DateTimeKind.Utc);
		}

		/// <summary>
		/// </summary>
		/// <param name="dateTime"></param>
		/// <param name="convertToUtc"></param>
		/// <returns></returns>
		/// <created author="laurentiu.macovei" date="Mon, 13 Feb 2012 17:26:47 GMT"/>
		internal static long ConvertDateTimeToJavaScriptTicks(DateTime dateTime, bool convertToUtc)
		{
			long ticks = convertToUtc ? ToUniversalTicks(dateTime) : dateTime.Ticks;
			return UniversialTicksToJavaScriptTicks(ticks);
		}
		/// <summary>
		/// </summary>
		/// <param name="dateTime"></param>
		/// <returns></returns>
		/// <created author="laurentiu.macovei" date="Mon, 13 Feb 2012 17:26:47 GMT"/>
		private static long ToUniversalTicks(DateTime dateTime)
		{
			if (dateTime.Kind == DateTimeKind.Utc)
			{
				return dateTime.Ticks;
			}
			return ToUniversalTicks(dateTime, dateTime.GetUtcOffset());
		}

		/// <summary>
		/// </summary>
		/// <param name="universialTicks"></param>
		/// <returns></returns>
		/// <created author="laurentiu.macovei" date="Mon, 13 Feb 2012 17:26:47 GMT"/>
		private static long UniversialTicksToJavaScriptTicks(long universialTicks)
		{
			return ((universialTicks - InitialJavaScriptDateTicks) / 10000L);
		}

		/// <summary>
		/// </summary>
		/// <param name="d"></param>
		/// <returns></returns>
		/// <created author="laurentiu.macovei" date="Mon, 13 Feb 2012 17:26:47 GMT"/>
		public static TimeSpan GetUtcOffset(this DateTime d)
		{
			return TimeZoneInfo.Local.GetUtcOffset(d);
		}


		/// <summary>
		/// </summary>
		/// <param name="response"></param>
		/// <param name="name"></param>
		/// <param name="rememberMe"></param>
		/// <param name="UserData"></param>
		/// <returns></returns>
		/// <created author="laurentiu.macovei" date="Wed, 25 Jan 2012 22:13:56 GMT"/>
		public static HttpCookie SetAuthCookie(this HttpResponse response, string name, bool rememberMe, string UserData)
		{
			//http://www.danharman.net/2011/07/07/storing-custom-data-in-forms-authentication-tickets/
			var cookie = FormsAuthentication.GetAuthCookie(name, rememberMe);
			var ticket = FormsAuthentication.Decrypt(cookie.Value);
			var newTicket = new FormsAuthenticationTicket(ticket.Version, ticket.Name,
				ticket.IssueDate, ticket.Expiration, ticket.IsPersistent,
				UserData, ticket.CookiePath);

			var encTicket = FormsAuthentication.Encrypt(newTicket);

			// Create the cookie.
			cookie = new HttpCookie(FormsAuthentication.FormsCookieName, encTicket);
			response.Cookies.Add(cookie);

			return cookie;
		}
		/// <summary>Retrieves the configured value for EnableReCapthca -&gt;AppSettings["reCaptcha:enable"]</summary>
		/// <created author="laurentiu.macovei" date="Fri, 24 Feb 2012 13:23:51 GMT"/>
		public static readonly bool EnableReCapthca = string.Equals(ConfigurationManager.AppSettings["reCaptcha:enable"], "true", StringComparison.OrdinalIgnoreCase);

		/// <summary>
		/// </summary>
		/// <param name="format"></param>
		/// <param name="provider"></param>
		/// <param name="args"></param>
		/// <returns></returns>
		/// <created author="laurentiu.macovei" date="Mon, 13 Feb 2012 17:32:52 GMT"/>
		public static string FormatWith(this string format, IFormatProvider provider, params object[] args)
		{
			if (format == null)
			{
				throw new ArgumentNullException("format");
			}
			return string.Format(provider, format, args);
		}
		/// <summary>
		/// </summary>
		/// <param name="format"></param>
		/// <param name="args"></param>
		/// <returns></returns>
		/// <created author="laurentiu.macovei" date="Mon, 13 Feb 2012 17:32:53 GMT"/>
		public static string FormatWith(this string format, params object[] args)
		{
			if (format == null)
			{
				throw new ArgumentNullException("format");
			}
			return string.Format(CultureInfo.InvariantCulture, format, args);
		}

		private static readonly JavaScriptDateTimeConverterWithoutDatePrefix _JavaScriptDateTimeConverter = new JavaScriptDateTimeConverterWithoutDatePrefix();
		/// <summary>Serializes the specified object to a JSON string.</summary>
		/// <param name="value">The object to serialize.</param>
		/// <returns>A JSON string representation of the object.</returns>
		/// <created author="laurentiu.macovei" date="Thu, 09 Feb 2012 18:30:19 GMT"/>
		public static string ToJson(this object value)
		{
			return JsonConvert.SerializeObject(value, _JavaScriptDateTimeConverter);
		}

		/// <summary>Serializes the specified object to a JSON string.</summary>
		/// <param name="value">The object to serialize.</param>
		/// <param name="formatting">Indicates how the output is formatted.</param>
		/// <returns>A JSON string representation of the object.</returns>
		/// <created author="laurentiu.macovei" date="Thu, 09 Feb 2012 18:30:20 GMT"/>
		public static string ToJson(this object value, Newtonsoft.Json.Formatting formatting)
		{
			return JsonConvert.SerializeObject(value, formatting, _JavaScriptDateTimeConverter);
		}

		/// <summary>Serializes the specified object to a JSON string using a collection of Newtonsoft.Json.JsonConverter.</summary>
		/// <param name="value">The object to serialize.</param>
		/// <param name="converters">A collection converters used while serializing.</param>
		/// <returns>A JSON string representation of the object.</returns>
		/// <created author="laurentiu.macovei" date="Thu, 09 Feb 2012 18:30:20 GMT"/>
		public static string ToJson(this object value, params JsonConverter[] converters)
		{
			return JsonConvert.SerializeObject(value, converters);
		}

		/// <summary>Serializes the specified object to a JSON string using a collection of Newtonsoft.Json.JsonConverter.</summary>
		/// <param name="value">The object to serialize.</param>
		/// <param name="formatting">Indicates how the output is formatted.</param>
		/// <param name="settings">
		/// The Newtonsoft.Json.JsonSerializerSettings used to serialize the object. 
		/// If this is null, default serialization settings will be is used. 
		/// </param>
		/// <returns>A JSON string representation of the object.</returns>
		/// <created author="laurentiu.macovei" date="Thu, 09 Feb 2012 18:30:20 GMT"/>
		public static string ToJson(this object value, Newtonsoft.Json.Formatting formatting, JsonSerializerSettings settings)
		{
			return JsonConvert.SerializeObject(value, formatting, settings);
		}

		/// <summary>Serializes the specified object to a JSON string using a collection of Newtonsoft.Json.JsonConverter.</summary>
		/// <param name="value">The object to serialize.</param>
		/// <param name="formatting">Indicates how the output is formatted.</param>
		/// <param name="converters">A collection converters used while serializing.</param>
		/// <returns>A JSON string representation of the object.</returns>
		/// <created author="laurentiu.macovei" date="Thu, 09 Feb 2012 18:30:20 GMT"/>
		public static string ToJson(this object value, Newtonsoft.Json.Formatting formatting, params JsonConverter[] converters)
		{
			return JsonConvert.SerializeObject(value, formatting, converters);
		}

	}
	/// <summary>
	/// Converts a <see cref="T:System.DateTime"/> to and from a JavaScript date constructor (e.g. new Date(52231943)).
	/// </summary>
	/// <created author="laurentiu.macovei" date="Mon, 13 Feb 2012 17:32:53 GMT"/>
	public class JavaScriptDateTimeConverterWithoutDatePrefix 
		: DateTimeConverterBase
	{
		/// <summary>Reads the JSON representation of the object.</summary>
		/// <param name="reader">The <see cref="T:Newtonsoft.Json.JsonReader"/>to read from.</param>
		/// <param name="objectType">Type of the object.</param>
		/// <param name="existingValue">The existing property value of the JSON that is being converted.</param>
		/// <param name="serializer">The calling serializer.</param>
		/// <returns>The object value.</returns>
		/// <created author="laurentiu.macovei" date="Mon, 13 Feb 2012 17:32:53 GMT"/>
		public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
		{
			var isNullableType = (objectType.IsGenericType && (objectType.GetGenericTypeDefinition() == typeof(Nullable<>)));

			Type t = isNullableType ? Nullable.GetUnderlyingType(objectType) : objectType;
			if (reader.TokenType == JsonToken.Null)
			{
				if (!isNullableType)
				{
					throw new Exception("Cannot convert null value to {0}.".FormatWith(CultureInfo.InvariantCulture, new object[] { objectType }));
				}
				return null;
			}
			bool hasDateCtor = false;
			if (!((reader.TokenType != JsonToken.StartConstructor) || !string.Equals(reader.Value.ToString(), "Date", StringComparison.Ordinal)))
			{
				hasDateCtor = true;
				reader.Read();
			}
			//else throw new Exception("Unexpected token or value when parsing date. Token: {0}, Value: {1}".FormatWith(CultureInfo.InvariantCulture, new object[] { reader.TokenType, reader.Value }));
			if (reader.TokenType != JsonToken.Integer)
			{
				throw new Exception("Unexpected token parsing date. Expected Integer, got {0}.".FormatWith(CultureInfo.InvariantCulture, new object[] { reader.TokenType }));
			}
			long ticks = (long)reader.Value;
			DateTime d = ExtendedMethods.ConvertJavaScriptTicksToDateTime(ticks);
			reader.Read();
			if (hasDateCtor && reader.TokenType != JsonToken.EndConstructor)
			{
				throw new Exception("Unexpected token parsing date. Expected EndConstructor, got {0}.".FormatWith(CultureInfo.InvariantCulture, new object[] { reader.TokenType }));
			}
			if (t == typeof(DateTimeOffset))
			{
				return new DateTimeOffset(d);
			}
			return d;
		}

		/// <summary>Writes the JSON representation of the object.</summary>
		/// <param name="writer">The <see cref="T:Newtonsoft.Json.JsonWriter"/>to write to.</param>
		/// <param name="value">The value.</param>
		/// <param name="serializer">The calling serializer.</param>
		/// <created author="laurentiu.macovei" date="Mon, 13 Feb 2012 17:32:53 GMT"/>
		public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
		{
			long ticks;
			if (value is DateTime)
			{
				ticks = ExtendedMethods.ConvertDateTimeToJavaScriptTicks(((DateTime)value).ToUniversalTime());
			}
			else
			{
				if (!(value is DateTimeOffset))
				{
					throw new Exception("Expected date object value.");
				}
				DateTimeOffset dateTimeOffset = (DateTimeOffset)value;
				ticks = ExtendedMethods.ConvertDateTimeToJavaScriptTicks(dateTimeOffset.ToUniversalTime().UtcDateTime);
			}
			writer.WriteValue(ticks);
		}
	}

	/// <summary>The KnockoutZone class</summary>
	/// <created author="laurentiu.macovei" date="Fri, 13 Jan 2012 02:35:10 GMT"/>
	public class KnockoutZone
		: IDisposable
	{
		private ControllerBase _Controller;
		private TagBuilder tagBuilder;
		private HtmlHelper _HtmlHelper;
		/// <summary>Creates a new instance of KnockoutZone</summary>
		/// <param name="controller"></param>
		/// <param name="htmlAttributes"></param>
		/// <param name="htmlHelper"></param>
		/// <param name="tag"></param>
		/// <created author="laurentiu.macovei" date="Fri, 13 Jan 2012 02:35:10 GMT"/>
		public KnockoutZone(HtmlHelper htmlHelper, ControllerBase controller, string tag = null, params object[] htmlAttributes)
		{
			this._Controller = controller;
			this._HtmlHelper = htmlHelper;
			var bag = controller.ViewBag;
			if (!string.IsNullOrWhiteSpace(tag))
			{
				tagBuilder = new TagBuilder(tag);
				_HtmlHelper.ViewContext.Writer.Write(tagBuilder.ToMvcHtmlString(TagRenderMode.StartTag));
			}
		}
		/// <summary>Closes the Knockout zone</summary>
		/// <created author="laurentiu.macovei" date="Fri, 13 Jan 2012 02:35:10 GMT"/>
		public void Dispose()
		{
			var bag = _Controller.ViewBag;
			bag.Knockout = (bag.Knockout ?? 0) - 1;
			if (tagBuilder != null)
				_HtmlHelper.ViewContext.Writer.Write(tagBuilder.ToMvcHtmlString(TagRenderMode.EndTag));
		}
	}
}